﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		JavascriptCombinedResult.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 // 
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Dexter.Caching;
using Dexter.Services;
using Dexter.Web.Mvc.Configuration;
using Dexter.Web.Mvc.Controllers;
using Yahoo.Yui.Compressor;

namespace Dexter.Web.Mvc.Results {
	public class JavascriptCombinedResult : ActionResult {
		readonly string key;
		IBlogTheme blogTheme;
		readonly ICacheProvider cacheService;
		readonly IResourceConfiguration resourceConfiguration;

		/// <summary>
		/// Initializes a new instance of the <see cref="JavascriptCombinedResult"/> class.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="resourceConfiguration">The resource configuration.</param>
		public JavascriptCombinedResult ( string key , IResourceConfiguration resourceConfiguration , ICacheProvider cacheService ) {
			if(key.IsNullOrEmpty (  ))
				throw new ArgumentException ( "Invalid key." );

			this.key = key;
			this.cacheService = cacheService;
			this.resourceConfiguration = resourceConfiguration;
		}

		/// <summary>
		/// 	Enables processing of the result of an action method by a custom type that inherits from the <see cref = "T:System.Web.Mvc.ActionResult" /> class.
		/// </summary>
		/// <param name = "context">The context in which the result is executed. The context information includes the controller, HTTP content, request context, and route data.</param>
		public override void ExecuteResult ( ControllerContext context ) {
			var c = ( ResourcesController ) context.Controller;

			blogTheme = c.CurrentTheme;

			var httpResponse = context.HttpContext.Response;

			var d = cacheService.Get <HashSet <string>> ( key );

			if ( d == null ) {
				httpResponse.StatusCode = ( int ) HttpStatusCode.NotFound;
				return;
			}

			string etag = string.Concat ( "\"", key, "\"" );
			string incomingEtag = context.HttpContext.Request.Headers["If-None-Match"];

			if (String.Compare ( incomingEtag, etag ) == 0) {
				httpResponse.Clear ( );
				httpResponse.StatusCode = ( int )HttpStatusCode.NotModified;
				httpResponse.SuppressContent = true;
			}

			var data = d.ToList ( );

			httpResponse.ContentEncoding = Encoding.UTF8;
			httpResponse.ContentType = "text/javascript";
			httpResponse.Cache.VaryByHeaders[ "Accept-Encoding" ] = true;
			httpResponse.Cache.SetCacheability ( HttpCacheability.Public );
			httpResponse.Cache.SetExpires ( DateTime.Now.AddDays ( resourceConfiguration.CacheFor.Days ) );
			httpResponse.Cache.SetMaxAge ( resourceConfiguration.CacheFor );
			httpResponse.Cache.SetRevalidation ( HttpCacheRevalidation.AllCaches );
			httpResponse.Cache.SetValidUntilExpires ( true );
			httpResponse.Cache.SetETag ( etag );
			httpResponse.Cache.SetCacheability ( HttpCacheability.Public );

			var compressJsOutput = cacheService.Get <string> ( "compressJsOutput" + key );
			var dependencyJsOutput = cacheService.Get <string[]> ( "dependencyJsOutput" + key );

			if ( dependencyJsOutput != null && compressJsOutput != null ) {
				httpResponse.AddFileDependencies ( dependencyJsOutput );
				httpResponse.Cache.SetLastModifiedFromFileDependencies ( );
				httpResponse.Write ( compressJsOutput );
				httpResponse.End ( );
				return;
			}

			StringBuilder sb = new StringBuilder ( );
			dependencyJsOutput = new string[data.Count];

			for ( int i = 0; i < data.Count; i++ ) {
				var filePath = blogTheme.GetScriptPath ( data[ i ] );
				if ( File.Exists ( filePath ) ) {
					dependencyJsOutput[ i ] = filePath;
					sb.Append ( File.ReadAllText ( filePath ) ).AppendLine ( );
				}
				else if (resourceConfiguration.ThrowExceptionOnMissingFiles) {
					throw new FileNotFoundException ( "File not found" , filePath );
				}
			}

			compressJsOutput = resourceConfiguration.Minify
			                   	? JavaScriptCompressor.Compress ( sb.ToString ( ) )
			                   	: sb.ToString ( );

			cacheService.Put ( "compressJsOutput" + key, compressJsOutput, resourceConfiguration.CacheFor );
			cacheService.Put ( "dependencyJsOutput" + key, dependencyJsOutput, resourceConfiguration.CacheFor );

			httpResponse.AddFileDependencies ( dependencyJsOutput );
			httpResponse.Cache.SetLastModifiedFromFileDependencies ( );
			httpResponse.Write ( compressJsOutput );
			httpResponse.End ( );
		}
	}
}
